home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 January: Mac OS SDK / Dev.CD Jan 98 SDK2.toast / Development Kits (Disc 2) / QuickDraw GX / Programming Stuff / GX Libraries / SelectionLibrary.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-03-31  |  33.8 KB  |  1,095 lines  |  [TEXT/MPS ]

  1.  
  2. /*
  3.     File:        SelectionLibrary.c
  4.  
  5.     Contains:    graphics libraries - SelectionHandle manipulation routines
  6.     
  7.     Written by:    Cary Clark, Georgiann Delaney, Michael Fairman, Dave Good, Robert Johnson, Keith McGreggor, Oliver Steele, David Van Brink, Chris Yerga
  8.     
  9.     Copyright:    © 1995 by Apple Computer, Inc., all rights reserved.
  10.  
  11.     Change History (most recent first):
  12.     
  13.          <2>      1/9/95    JD        changed 'boolean' to 'Boolean'
  14.          <1>      1/9/95    JD        First checked in.
  15.     Previous History:
  16.  
  17.         Date            Person        Action
  18.         ----            ------        ------
  19.         
  20.         911031        DGO            • Created module.
  21.         920130        DGO            • Canonicalized order for range selections.
  22.         940606        DGO            •    Minor bugfixes.
  23.  
  24. */
  25.  
  26.  
  27. #include <Types.h>
  28. #include <Memory.h>
  29. #include <OSUtils.h>
  30. #include <GXTypes.h>
  31. #include <GXLayout.h>
  32. #include <GXGraphics.h>
  33. #include "SelectionLibrary.h"
  34.  
  35. #define siSize sizeof(Selection)
  36. #define sioSize (siSize + sizeof(SelectionOffsetRange))
  37.  
  38. #define MyMin(a,b) (((a) < (b)) ? (a) : (b))
  39. #define MyMax(a,b) (((a) > (b)) ? (a) : (b))
  40.  
  41. typedef enum {
  42.     unionAll,
  43.     unionOne,
  44.     firstIsLow,
  45.     secondIsLow
  46.     } UnionOneState;
  47.  
  48. /* ---------------------------------------------------------------------------------- */
  49.  
  50. /* NewEmptySelection creates a Selection of type emptySelection. */
  51.  
  52. SelectionHandle NewEmptySelection()
  53.     {
  54.     Handle                h;
  55.     SelectionPtr    sip;
  56.     if ((h = NewHandle(siSize)) == nil) {GXPostGraphicsError(out_of_memory); return nil;}
  57.     sip = (SelectionPtr) *h;
  58.     sip->type = emptySelection;
  59.     return ((SelectionHandle) h);
  60.     } /* NewEmptySelection */
  61.  
  62. /* ---------------------------------------------------------------------------------- */
  63.  
  64. /* NewCaretSelection creates a Selection of type simpleCaret and fills it with the
  65.         specified offset and leadingEdge information. */
  66.  
  67. SelectionHandle NewCaretSelection(SelectionOffset offset, long leadingEdge)
  68.     {
  69.     Handle                h;
  70.     SelectionPtr    sip;
  71.     if ((h = NewHandle(siSize)) == nil) {GXPostGraphicsError(out_of_memory); return nil;}
  72.     sip = (SelectionPtr) *h;
  73.     sip->type = simpleCaret;
  74.     sip->data.caret.offset = offset;
  75.     sip->data.caret.leadingEdge = (short) leadingEdge;
  76.     return ((SelectionHandle) h);
  77.     } /* NewCaretSelection */
  78.  
  79. /* ---------------------------------------------------------------------------------- */
  80.  
  81. /* NewRangeSelection creates a Selection of type simpleRange and sets it to a single
  82.         range, using the specified data. */
  83.  
  84. SelectionHandle NewRangeSelection(SelectionOffsetRange *range)
  85.     {
  86.     Handle                                h;
  87.     SelectionOffsetRange    sortedRange;
  88.     SelectionPtr                    sip;
  89.     if (range == nil) {GXPostGraphicsError(parameter_is_nil); return nil;}
  90.     if (range->minOffset == range->maxOffset)
  91.         return NewCaretSelection(range->minOffset, false);
  92.     else if (range->minOffset != selectionExtremeEdge && range->maxOffset != selectionExtremeEdge && range->minOffset > range->maxOffset)
  93.         {
  94.         sortedRange.minOffset = range->maxOffset;
  95.         sortedRange.maxOffset = range->minOffset;
  96.         range = &sortedRange;
  97.         }
  98.     if ((h = NewHandle(sioSize)) == nil) {GXPostGraphicsError(out_of_memory); return nil;}
  99.     sip = (SelectionPtr) *h;
  100.     sip->type = simpleRange;
  101.     sip->data.range.rangeCount = 1;
  102.     sip->data.range.ranges[0] = *range;
  103.     return ((SelectionHandle) h);
  104.     } /* NewRangeSelection */
  105.  
  106. /* ---------------------------------------------------------------------------------- */
  107.  
  108. /* NewFullSelection creates a Selection of type simpleRange and sets it to a single
  109.         range, with both ends set to the selectionExtremeEdge value. */
  110.  
  111. SelectionHandle NewFullSelection()
  112.     {
  113.     SelectionOffsetRange    myRange;
  114.     myRange.minOffset = myRange.maxOffset = selectionExtremeEdge;
  115.     return NewRangeSelection(&myRange);
  116.     } /* NewFullSelection */
  117.  
  118. /* ---------------------------------------------------------------------------------- */
  119.  
  120. /* NewStartSelection creates a Selection of type simpleRange and sets it to a single
  121.         range, with the start set to selectionExtremeEdge and the end set to the specified
  122.         offset. */
  123.  
  124. SelectionHandle NewStartSelection(SelectionOffset toOffset)
  125.     {
  126.     SelectionOffsetRange    myRange;
  127.     myRange.minOffset = selectionExtremeEdge;
  128.     myRange.maxOffset = toOffset;
  129.     return NewRangeSelection(&myRange);
  130.     } /* NewStartSelection */
  131.  
  132. /* ---------------------------------------------------------------------------------- */
  133.  
  134. /* NewEndSelection creates a Selection of type simpleRange and sets it to a single
  135.         range, with the start set to the specified offset and the end set to the
  136.         selectionExtremeEdge value. */
  137.  
  138. SelectionHandle NewEndSelection(SelectionOffset fromOffset)
  139.     {
  140.     SelectionOffsetRange    myRange;
  141.     myRange.minOffset = fromOffset;
  142.     myRange.maxOffset = selectionExtremeEdge;
  143.     return NewRangeSelection(&myRange);
  144.     } /* NewEndSelection */
  145.  
  146. /* ---------------------------------------------------------------------------------- */
  147.  
  148. /* SetEmptySelection takes an existing Selection and removes its contents, turning it
  149.         into a Selection of type emptySelection. */
  150.  
  151. void SetEmptySelection(SelectionHandle selection)
  152.     {
  153.     SelectionPtr    sip;
  154.     if (selection == nil || (sip = *selection) == nil)
  155.         {GXPostGraphicsError(parameter_is_nil); return;}
  156.     sip->type = emptySelection;
  157.     } /* SetEmptySelection */
  158.  
  159. /* ---------------------------------------------------------------------------------- */
  160.  
  161. /* SetCaretSelection takes an existing Selection and removes its contents, turning it
  162.         into a Selection of type simpleCaret and copying the specified data into it. */
  163.  
  164. void SetCaretSelection(SelectionHandle selection, SelectionOffset offset, long leadingEdge)
  165.     {
  166.     SelectionPtr    sip;
  167.     if (selection == nil || (sip = *selection) == nil)
  168.         {GXPostGraphicsError(parameter_is_nil); return;}
  169.     sip->type = simpleCaret;
  170.     sip->data.caret.offset = offset;
  171.     sip->data.caret.leadingEdge = (short) leadingEdge;
  172.     } /* SetCaretSelection */
  173.  
  174. /* ---------------------------------------------------------------------------------- */
  175.  
  176. /* SetRangeSelection takes an existing Selection and removes its contents, turning it
  177.         into a Selection of type simpleRange containing a single range with the specified
  178.         offsets. */
  179.  
  180. void SetRangeSelection(SelectionHandle selection, SelectionOffsetRange *range)
  181.     {
  182.     SelectionOffsetRange    sortedRange;
  183.     SelectionPtr                    sip;
  184.     if (selection == nil || (sip = *selection) == nil)
  185.         {GXPostGraphicsError(parameter_is_nil); return;}
  186.     if (range->minOffset == range->maxOffset)
  187.         {
  188.         SetCaretSelection(selection, range->minOffset, false);
  189.         return;
  190.         }
  191.     if (GetHandleSize((Handle) selection) < sioSize)
  192.         { /* need to grow handle */
  193.         SetHandleSize((Handle) selection, sioSize);
  194.         if (MemError())
  195.             {GXPostGraphicsError(out_of_memory); return;}
  196.         sip = *selection; /* might have moved... */
  197.         } /* end need to grow handle */
  198.     if (range->minOffset > range->maxOffset)
  199.         {
  200.         sortedRange.minOffset = range->maxOffset;
  201.         sortedRange.maxOffset = range->minOffset;
  202.         range = &sortedRange;
  203.         }
  204.     sip->type = simpleRange;
  205.     sip->data.range.rangeCount = 1;
  206.     sip->data.range.ranges[0] = *range;
  207.     } /* SetRangeSelection */
  208.  
  209. /* ---------------------------------------------------------------------------------- */
  210.  
  211. /* SetFullSelection takes an existing Selection and removes its contents, turning it
  212.         into a Selection of type simpleRange containing a single range with both offsets
  213.         set to the selectionExtremeEdge value. */
  214.  
  215. void SetFullSelection(SelectionHandle selection)
  216.     {
  217.     SelectionOffsetRange    myRange;
  218.     myRange.minOffset = myRange.maxOffset = selectionExtremeEdge;
  219.     SetRangeSelection(selection, &myRange);
  220.     } /* SetFullSelection */
  221.  
  222. /* ---------------------------------------------------------------------------------- */
  223.  
  224. /* SetStartSelection takes an existing Selection and removes its contents, turning it into
  225.         a Selection of type simpleRange containing a single range with the start offset set
  226.         to the selectionExtremeEdge value and the end offset set to the specified value. */
  227.  
  228. void SetStartSelection(SelectionHandle selection, SelectionOffset toOffset)
  229.     {
  230.     SelectionOffsetRange    myRange;
  231.     myRange.minOffset = selectionExtremeEdge;
  232.     myRange.maxOffset = toOffset;
  233.     SetRangeSelection(selection, &myRange);
  234.     } /* SetStartSelection */
  235.  
  236. /* ---------------------------------------------------------------------------------- */
  237.  
  238. /* SetEndSelection takes an existing Selection and removes its contents, turning it into
  239.         a Selection of type simpleRange containing a single range with the start offset set
  240.         to the specified value and the end offset set to the selectionExtremeEdge value. */
  241.  
  242. void SetEndSelection(SelectionHandle selection, SelectionOffset fromOffset)
  243.     {
  244.     SelectionOffsetRange    myRange;
  245.     myRange.minOffset = fromOffset;
  246.     myRange.maxOffset = selectionExtremeEdge;
  247.     SetRangeSelection(selection, &myRange);
  248.     } /* SetEndSelection */
  249.  
  250. /* ---------------------------------------------------------------------------------- */
  251.  
  252. /* DisposeSelection disposes of the storage associated with a Selection. */
  253.  
  254. void DisposeSelection(SelectionHandle selection)
  255.     {
  256.     if (selection == nil) {GXPostGraphicsError(parameter_is_nil); return;}
  257.     else DisposeHandle((Handle) selection);
  258.     } /* DisposeSelection */
  259.  
  260. /* ---------------------------------------------------------------------------------- */
  261.  
  262. /* GetSelectionType allows procedural access to the type of a Selection. */
  263.  
  264. SelectionType GetSelectionType(SelectionHandle selection)
  265.     {
  266.     SelectionPtr    sip;
  267.     if (selection == nil || (sip = *selection) == nil)
  268.         {GXPostGraphicsError(parameter_is_nil); return emptySelection;}
  269.     else return sip->type;
  270.     } /* GetSelectionType */
  271.  
  272. /* ---------------------------------------------------------------------------------- */
  273.  
  274. /* GetCaretSelection allows procedural access to the character offset and leadingEdge
  275.         value associated with a Selection of type simpleCaret. */
  276.  
  277. SelectionOffset GetCaretSelection(SelectionHandle selection, Boolean *leadingEdge)
  278.     {
  279.     SelectionPtr    sip;
  280.     if (selection == nil || (sip = *selection) == nil)
  281.         {GXPostGraphicsError(parameter_is_nil); return 0;}
  282.     if (sip->type != simpleCaret) 
  283.         {
  284. #ifdef debugging
  285.         GXPostGraphicsError(parameter_out_of_range);
  286. #endif
  287.         return 0;
  288.         }
  289.     /* It is not an error for leadingEdge to be nil; in this case, we assume the client
  290.             wasn't interested in the state of the leadingEdge flag. */
  291.     if (leadingEdge) *leadingEdge = (Boolean) sip->data.caret.leadingEdge;
  292.     return sip->data.caret.offset;
  293.     } /* GetCaretSelection */
  294.  
  295. /* ---------------------------------------------------------------------------------- */
  296.  
  297. /* GetRangeSelection allows procedural access to the SelectionOffsetRange value(s)
  298.         associated with a Selection of type simpleRange or discontiguousRange. It returns
  299.         the total byte size needed for the resulting SelectionRanges structure; it returns
  300.         this size even if the given selectionRanges argument is nil. */
  301.  
  302. long GetRangeSelection(SelectionHandle selection, SelectionRanges *selectionRanges)
  303.     {
  304.     long                                    totalSize;
  305.     SelectionPtr                    sip;
  306.     SelectionOffsetRange    *pDst, *pSrc;
  307.     short                                 i;
  308.     if (selection == nil || (sip = *selection) == nil)
  309.         {GXPostGraphicsError(parameter_is_nil); return 0;}
  310.     if (sip->type != simpleRange && sip->type != discontiguousRange)
  311.         {
  312. #ifdef debugging
  313.         GXPostGraphicsError(parameter_out_of_range); 
  314. #endif
  315.         return 0;
  316.         }
  317.     totalSize = sizeof(SelectionRanges) + (sip->data.range.rangeCount * sizeof(SelectionOffsetRange));
  318.     if (selectionRanges)
  319.         { /* copy the data */
  320.         selectionRanges->rangeCount = sip->data.range.rangeCount;
  321.         i = (short) selectionRanges->rangeCount;
  322.         pDst = &selectionRanges->ranges[0];
  323.         pSrc = &sip->data.range.ranges[0];
  324.         while (i--) *pDst++ = *pSrc++;
  325.         } /* end copy the data */
  326.     return totalSize;
  327.     } /* GetRangeSelection */
  328.  
  329. /* ---------------------------------------------------------------------------------- */
  330.  
  331. /* XXX Still don't have "equalToSelection" logic in here... XXX */
  332.  
  333. /* RangeInSelection returns an indication of the degree to which the specified offset
  334.         range is contained in the specified Selection. */
  335.  
  336. SelectionMatch RangeInSelection(SelectionHandle selection, SelectionOffsetRange *range)
  337.     {
  338.     SelectionHandle     thisRange;
  339.     SelectionMatch        retVal;
  340.     SelectionPtr            sip;
  341.     
  342.     thisRange = NewRangeSelection(range);
  343.     SectSelection(thisRange, selection);
  344.     sip = *thisRange;
  345.     switch (sip->type)
  346.         {
  347.         case emptySelection:
  348.         case simpleCaret: /* "simpleCaret" hadn't better happen here... */
  349.             retVal = notInSelection;
  350.             break;
  351.         case simpleRange:
  352.             if (range->minOffset == sip->data.range.ranges[0].minOffset && range->maxOffset == sip->data.range.ranges[0].maxOffset)
  353.                 retVal = fullyInSelection;
  354.             else retVal = partlyInSelection;
  355.             break;
  356.         case discontiguousRange:
  357.             retVal = partlyInSelection; /* is this correct? */
  358.             break;
  359.         }
  360.     
  361.     DisposeSelection(thisRange);
  362.     return retVal;
  363.     } /* RangeInSelection */
  364.  
  365. /* ---------------------------------------------------------------------------------- */
  366.  
  367. /* IsFullSelection determines whether a Selection has the special selectionExtremeEdge
  368.         value as both its minOffset and maxOffset values. */
  369.  
  370. static Boolean IsFullSelection(SelectionHandle s)
  371.     {
  372.     SelectionPtr                    sip;
  373.     SelectionOffsetRange    *sor;
  374.     
  375.     if (s == nil || (sip = *s) == nil)
  376.         {GXPostGraphicsError(parameter_is_nil); return false;}
  377.     if (sip->type != simpleRange && sip->type != discontiguousRange)
  378.         return false;
  379.     sor = &sip->data.range.ranges[0];
  380.     
  381.     if (sor->minOffset == selectionExtremeEdge && sor->maxOffset == selectionExtremeEdge)
  382.         return true;
  383.     else
  384.         return false;
  385.     
  386.     } /* IsFullSelection */
  387.  
  388. /* ---------------------------------------------------------------------------------- */
  389.  
  390. void InvertSelection(SelectionHandle dest)
  391.     {
  392.     Handle                                tempHandle;
  393.     SelectionPtr                    destSIP;
  394.     SelectionOffsetRange    *newSOR, *nextSOR, *sor;
  395.     short                                 count, newCount;
  396.     
  397.     if (dest == nil || (destSIP = *dest) == nil)
  398.         {GXPostGraphicsError(parameter_is_nil); return;}
  399.     if (destSIP->type == simpleCaret)
  400.         {
  401. #ifdef debugging
  402.         GXPostGraphicsError(parameter_out_of_range);
  403. #endif
  404.         return;
  405.         }
  406.     
  407.     if (IsFullSelection(dest)) SetEmptySelection(dest);
  408.     else if (destSIP->type == emptySelection) SetFullSelection(dest);
  409.     else
  410.         { /* neither empty nor full */
  411.         tempHandle = NewHandle((destSIP->data.range.rangeCount + 1) * sizeof(SelectionOffsetRange));
  412.         if (tempHandle == nil)
  413.             {GXPostGraphicsError(out_of_memory); return;}
  414.         destSIP = *dest;    /* memory might have moved */
  415.         sor = &destSIP->data.range.ranges[0];
  416.         nextSOR = sor + 1;
  417.         count = (short) destSIP->data.range.rangeCount;
  418.         newSOR = (SelectionOffsetRange *) *tempHandle;
  419.         newCount = 0;
  420.         
  421.         /* At some gxPoint, we may want to deal with the problem that a non open-ended
  422.                 selection that starts at zero doesn't get restored after two inversions. */
  423.         
  424.         /* If first is not open-ended, add [selectionExtremeEdge, first.minOffset] */
  425.         if (sor->minOffset != selectionExtremeEdge)
  426.             {
  427.             newSOR->minOffset = selectionExtremeEdge;
  428.             (newSOR++)->maxOffset = sor->minOffset;
  429.             newCount++;
  430.             }
  431.         /* Start main loop */
  432.         while (--count) /* "--count" insures n-1 iterations */
  433.             { /* main loop */
  434.             newSOR->minOffset = sor->maxOffset;
  435.             (newSOR++)->maxOffset = nextSOR->minOffset;
  436.             newCount++;
  437.             sor = nextSOR++;
  438.             } /* end main loop */
  439.         /* If last is not open-ended, add [last.maxOffset, selectionExtremeEdge] */
  440.         if (sor->maxOffset != selectionExtremeEdge)
  441.             {
  442.             newSOR->minOffset = sor->maxOffset;
  443.             newSOR->maxOffset = selectionExtremeEdge;
  444.             newCount++;
  445.             }
  446.         /* Now change the size of dest (if needed) and store the new SORs into it. */
  447.         if (newCount > (short) destSIP->data.range.rangeCount)
  448.             {
  449.             SetHandleSize((Handle) dest, siSize + newCount * sizeof(SelectionOffsetRange));
  450.             destSIP = *dest;    /* memory might have moved */
  451.             }
  452.         destSIP->data.range.rangeCount = newCount;
  453.         destSIP->type = (SelectionType) ((newCount == 1) ? simpleRange : discontiguousRange);
  454.         sor = &destSIP->data.range.ranges[0];
  455.         newSOR = (SelectionOffsetRange *) *tempHandle;
  456.         while (newCount--) *sor++ = *newSOR++;
  457.         DisposeHandle(tempHandle);
  458.         } /* end neither empty nor full */
  459.     
  460.     } /* InvertSelection */
  461.  
  462. /* ---------------------------------------------------------------------------------- */
  463.  
  464. void ShiftSelection(SelectionHandle dest, SelectionOffset delta)
  465.     {
  466.     SelectionOffsetRange    *sor;
  467.     SelectionPtr                    destSIP;
  468.     short                                 count;
  469.     
  470.     if (dest == nil || (destSIP = *dest) == nil)
  471.         {GXPostGraphicsError(parameter_is_nil); return;}
  472.     
  473.     switch (destSIP->type)
  474.         {
  475.         case emptySelection:
  476.             break;
  477.         case simpleCaret:
  478.             destSIP->data.caret.offset += delta;
  479.             break;
  480.         case simpleRange:
  481.         case discontiguousRange:
  482.             count = (short) destSIP->data.range.rangeCount;
  483.             sor = &destSIP->data.range.ranges[0];
  484.             while (count--)
  485.                 {
  486.                 if (sor->minOffset != selectionExtremeEdge) sor->minOffset += delta;
  487.                 if (sor->maxOffset != selectionExtremeEdge) sor->maxOffset += delta;
  488.                 sor++;
  489.                 }
  490.             break;
  491.         } /* end switch */
  492.     
  493.     } /* ShiftSelection */
  494.  
  495. /* ---------------------------------------------------------------------------------- */
  496.  
  497. /* GrowSI is an internal work function used to grow a Selection to allow more
  498.         space for SelectionOffsetRanges. */
  499.  
  500. static void GrowSI(
  501.     Handle                                h,
  502.     short                                 *maxAllocated,
  503.     SelectionPtr                    *sip,
  504.     SelectionOffsetRange    **dest,
  505.     SelectionOffsetRange    **prevDest)
  506.  
  507.     {
  508.     short oldMax = *maxAllocated;
  509.     *maxAllocated <<= 1;
  510.     SetHandleSize(h, siSize + (*maxAllocated) * sizeof(SelectionOffsetRange));
  511.     if (MemError() != noErr) GXPostGraphicsError(out_of_memory);
  512.     *sip = (SelectionPtr) *h;
  513.     *dest = (*sip)->data.range.ranges + oldMax;
  514.     *prevDest = *dest - 1;
  515.     } /* GrowSI */
  516.  
  517. /* ---------------------------------------------------------------------------------- */
  518.  
  519. /* UnionOne takes two SelectionOffsetRanges and determines if their union is representable
  520.         in a single resulting SelectionOffsetRange. If it is not, the dest value is not set,
  521.         and a value is returned that indicates whether the first or second argument starts
  522.         earlier. If it is, the dest value is set to the union, and the unionOne value is
  523.         returned. */
  524.  
  525. static UnionOneState UnionOne(
  526.     SelectionOffsetRange    *sor1,
  527.     SelectionOffsetRange    *sor2,
  528.     SelectionOffsetRange    *dest)
  529.  
  530.     {
  531.     short                 selector;
  532.     UnionOneState retVal;
  533.     
  534.     selector = (short) ((sor1->minOffset == selectionExtremeEdge) << 3);
  535.     selector += (short) ((sor1->maxOffset == selectionExtremeEdge) << 2);
  536.     selector += (short) ((sor2->minOffset == selectionExtremeEdge) << 1);
  537.     selector += (sor2->maxOffset == selectionExtremeEdge);
  538.     
  539.     switch (selector)
  540.         {
  541.         
  542.         /* Case 0 is where neither SelectionOffsetRange is open-ended */
  543.         
  544.         case 0:
  545.             if (sor1->minOffset < sor2->minOffset)
  546.                 if (sor2->minOffset <= sor1->maxOffset)
  547.                     {
  548.                     dest->minOffset = sor1->minOffset;
  549.                     dest->maxOffset = MyMax(sor1->maxOffset, sor2->maxOffset);
  550.                     retVal = unionOne;
  551.                     }
  552.                 else retVal = firstIsLow;
  553.             else
  554.                 if (sor1->minOffset <= sor2->maxOffset)
  555.                     {
  556.                     dest->minOffset = sor2->minOffset;
  557.                     dest->maxOffset = MyMax(sor1->maxOffset, sor2->maxOffset);
  558.                     retVal = unionOne;
  559.                     }
  560.                 else retVal = secondIsLow;
  561.             break;
  562.         
  563.         /* Case 1 is where the second SelectionOffsetRange is open on the right. */
  564.         
  565.         case 1:
  566.             if (sor2->minOffset > sor1->maxOffset) retVal = firstIsLow;
  567.             else
  568.                 {
  569.                 dest->minOffset = MyMin(sor1->minOffset, sor2->minOffset);
  570.                 dest->maxOffset = selectionExtremeEdge;
  571.                 retVal = unionOne;
  572.                 }
  573.             break;
  574.         
  575.         /* Case 2 is where the second SelectionOffsetRange is open on the left. */
  576.         
  577.         case 2:
  578.             if (sor2->maxOffset < sor1->minOffset) retVal = secondIsLow;
  579.             else
  580.                 {
  581.                 dest->minOffset = selectionExtremeEdge;
  582.                 dest->maxOffset = MyMax(sor1->maxOffset, sor2->maxOffset);
  583.                 retVal = unionOne;
  584.                 }
  585.             break;
  586.         
  587.         /* Cases 3, 7, and 11 thru 15 are where one (or both) are full selections. */
  588.         
  589.         case 3:
  590.         case 7:
  591.         case 11:
  592.         case 12:
  593.         case 13:
  594.         case 14:
  595.         case 15:
  596.             dest->minOffset = dest->maxOffset = selectionExtremeEdge;
  597.             retVal = unionAll;
  598.             break;
  599.         
  600.         /* Case 4 is where the first SelectionOffsetRange is open on the right. */
  601.         
  602.         case 4:
  603.             if (sor1->minOffset > sor2->maxOffset) retVal = secondIsLow;
  604.             else
  605.                 {
  606.                 dest->minOffset = MyMin(sor1->minOffset, sor2->minOffset);
  607.                 dest->maxOffset = selectionExtremeEdge;
  608.                 retVal = unionOne;
  609.                 }
  610.             break;
  611.         
  612.         /* Case 5 is where both SelectionOffsetRanges are open on the right. */
  613.         
  614.         case 5:
  615.             dest->minOffset = MyMin(sor1->minOffset, sor2->minOffset);
  616.             dest->maxOffset = selectionExtremeEdge;
  617.             retVal = unionOne;
  618.             break;
  619.         
  620.         /* Case 6 is where the first SelectionOffsetRange is open on the right and the
  621.                 second SelectionOffsetRange is open on the right. */
  622.         
  623.         case 6:
  624.             if (sor1->minOffset > sor2->maxOffset) retVal = secondIsLow;
  625.             else
  626.                 {
  627.                 dest->minOffset = dest->maxOffset = selectionExtremeEdge;
  628.                 retVal = unionAll;
  629.                 }
  630.             break;
  631.         
  632.         /* Case 8 is where the first SelectionOffsetRange is open on the left. */
  633.         
  634.         case 8:
  635.             if (sor1->maxOffset < sor2->minOffset) retVal = firstIsLow;
  636.             else
  637.                 {
  638.                 dest->minOffset = selectionExtremeEdge;
  639.                 dest->maxOffset = MyMax(sor1->maxOffset, sor2->maxOffset);
  640.                 retVal = unionOne;
  641.                 }
  642.             break;
  643.         
  644.         /* Case 9 is where the first SelectionOffsetRange is open on the left and the
  645.                 second SelectionOffsetRange is open on the left. */
  646.         
  647.         case 9:
  648.             if (sor2->minOffset > sor1->maxOffset) retVal = firstIsLow;
  649.             else
  650.                 {
  651.                 dest->minOffset = dest->maxOffset = selectionExtremeEdge;
  652.                 retVal = unionAll;
  653.                 }
  654.             break;
  655.         
  656.         /* Case 10 is where both SelectionOffsetRanges are open on the left. */
  657.         
  658.         case 10:
  659.             dest->minOffset = selectionExtremeEdge;
  660.             dest->maxOffset = MyMax(sor1->maxOffset, sor2->maxOffset);
  661.             retVal = unionOne;
  662.             break;
  663.         
  664.         } /* end switch */
  665.     
  666.     return retVal;
  667.     } /* UnionOne */
  668.  
  669. /* ---------------------------------------------------------------------------------- */
  670.  
  671. void UnionSelection(SelectionHandle dest, SelectionHandle source)
  672.     {
  673.     Handle                                tempHandle;
  674.     long                                    destCount, sor1Count, sor2Count;
  675.     SelectionOffsetRange    *destSOR, *prevDestSOR, *sor1, *sor2, workSORSpace, workSORSpace2;
  676.     SelectionPtr                    destSIP, newSIP, sourceSIP;
  677.     short                                 maxAllocated = 10;
  678.     Size                                    newSize, sourceSize;
  679.     
  680.     /* Allocate the memory first, since we want it to be the last thing that moves mem. */
  681.     
  682.     newSize = siSize + maxAllocated * sizeof(SelectionOffsetRange);
  683.     tempHandle = NewHandle(newSize);
  684.     if (tempHandle == nil)
  685.         {GXPostGraphicsError(out_of_memory); return;}
  686.     destCount = 0;
  687.     newSIP = (SelectionPtr) *tempHandle;
  688.     
  689.     /* Validate inputs. */
  690.     
  691.     if (dest == nil || (destSIP = *dest) == nil)
  692.         {GXPostGraphicsError(parameter_is_nil); DisposeHandle(tempHandle); return;}
  693.     if (source == nil || (sourceSIP = *source) == nil)
  694.         {GXPostGraphicsError(parameter_is_nil); DisposeHandle(tempHandle); return;}
  695.     if (destSIP->type == simpleCaret || sourceSIP->type == simpleCaret)
  696.         {
  697. #ifdef debugging
  698.         GXPostGraphicsError(parameter_out_of_range);
  699. #endif
  700.         DisposeHandle(tempHandle);
  701.         return;
  702.         }
  703.     
  704.     /* If both the Selections are empty, just return. */
  705.     
  706.     if ((destSIP->type == emptySelection) && (sourceSIP->type == emptySelection))
  707.         {DisposeHandle(tempHandle); return;}
  708.     
  709.     /* Handle cases where one (but not both) selection is empty. If dest is empty, this
  710.             means just copy source to dest. If source is empty, just return. */
  711.     
  712.     if ((destSIP->type == emptySelection) && (sourceSIP->type != emptySelection))
  713.         { /* copy source to dest */
  714.         sourceSize = GetHandleSize((Handle) source);
  715.         SetHandleSize((Handle) dest, sourceSize);
  716.         if (MemError() != noErr) GXPostGraphicsError(out_of_memory);
  717.         else BlockMove(*((Handle) source), *((Handle) dest), sourceSize); /* mem might have moved! */
  718.         DisposeHandle(tempHandle);
  719.         return;
  720.         } /* end copy source to dest */
  721.     else if ((destSIP->type != emptySelection) && (sourceSIP->type == emptySelection))
  722.         {DisposeHandle(tempHandle); return;}
  723.     
  724.     /* At this gxPoint both selections are range selections. We can therefore begin to walk
  725.             down the two selections in parallel, creating the new selection from the combo.
  726.             We "seed" the process by creating the first new entry before entering the loop;
  727.             this lets us get some of the testing code out of the loop proper. */
  728.     
  729.     prevDestSOR = destSOR = &newSIP->data.range.ranges[0];
  730.     sor1 = &destSIP->data.range.ranges[0];
  731.     sor2 = &sourceSIP->data.range.ranges[0];
  732.     sor1Count = destSIP->data.range.rangeCount;
  733.     sor2Count = sourceSIP->data.range.rangeCount;
  734.     destCount = 1;
  735.     
  736.     HLock((Handle) dest);
  737.     HLock((Handle) source);
  738.     
  739.     switch (UnionOne(sor1, sor2, &workSORSpace))
  740.         {
  741.         case unionAll:
  742.             *destSOR = workSORSpace;
  743.             sor1Count = sor2Count = 0;    /* forces following loop to be skipped */
  744.             break;
  745.         case unionOne:
  746.             *destSOR++ = workSORSpace;
  747.             sor1Count--; sor1++;
  748.             sor2Count--; sor2++;
  749.             break;
  750.         case firstIsLow:
  751.             *destSOR++ = *sor1++;
  752.             sor1Count--;
  753.             break;
  754.         case secondIsLow:
  755.             *destSOR++ = *sor2++;
  756.             sor2Count--;
  757.             break;
  758.         } /* end switch */
  759.     
  760.     while ((sor1Count > 0) || (sor2Count > 0))
  761.         { /* main merge loop */
  762.         if ((sor1Count > 0) && (sor2Count > 0))
  763.             { /* still have both */
  764.             switch (UnionOne(sor1, sor2, &workSORSpace))
  765.                 {
  766.                 case unionAll:
  767.                     newSIP->data.range.ranges[0] = workSORSpace;
  768.                     destCount = 1;
  769.                     sor1Count = sor2Count = 0;
  770.                     break;
  771.                 case unionOne:
  772.                     switch (UnionOne(prevDestSOR, &workSORSpace, &workSORSpace2))
  773.                         {
  774.                         case unionAll:
  775.                             newSIP->data.range.ranges[0] = workSORSpace2;
  776.                             destCount = 1;
  777.                             sor1Count = sor2Count = 1;    /* dec'd to 0 below, forces loop exit */
  778.                             break;
  779.                         case unionOne:
  780.                             *prevDestSOR = workSORSpace2;
  781.                             break;
  782.                         case firstIsLow:
  783.                             if (destCount == maxAllocated)
  784.                                 GrowSI(tempHandle, &maxAllocated, &newSIP, &destSOR, &prevDestSOR);
  785.                             *destSOR = workSORSpace;
  786.                             destCount++;
  787.                             prevDestSOR = destSOR++;
  788.                             break;
  789.                         case secondIsLow:
  790.                             GXPostGraphicsError(internal_layout_error);
  791.                             sor1Count = sor2Count = 1;    /* dec'd to 0 below, forces loop exit */
  792.                             break;
  793.                         } /* end switch */
  794.                     sor1++; sor2++;
  795.                     sor1Count--; sor2Count--;
  796.                     break;
  797.                 case firstIsLow:
  798.                     switch (UnionOne(prevDestSOR, sor1, &workSORSpace))
  799.                         {
  800.                         case unionAll:
  801.                             newSIP->data.range.ranges[0] = workSORSpace;
  802.                             destCount = 1;
  803.                             sor1Count = 1;    /* dec'd to 0 below, forces loop exit */
  804.                             sor2Count = 0;
  805.                             break;
  806.                         case unionOne:
  807.                             *prevDestSOR = workSORSpace;
  808.                             break;
  809.                         case firstIsLow:
  810.                             if (destCount == maxAllocated)
  811.                                 GrowSI(tempHandle, &maxAllocated, &newSIP, &destSOR, &prevDestSOR);
  812.                             *destSOR = *sor1;
  813.                             destCount++;
  814.                             prevDestSOR = destSOR++;
  815.                             break;
  816.                         case secondIsLow:
  817.                             GXPostGraphicsError(internal_layout_error);
  818.                             sor1Count = 1;    /* dec'd to 0 below, forces loop exit */
  819.                             break;
  820.                         } /* end switch */
  821.                     sor1++;
  822.                     sor1Count--;
  823.                     break;
  824.                 case secondIsLow:
  825.                     switch (UnionOne(prevDestSOR, sor2, &workSORSpace))
  826.                         {
  827.                         case unionAll:
  828.                             newSIP->data.range.ranges[0] = workSORSpace;
  829.                             destCount = 1;
  830.                             sor1Count = 0;
  831.                             sor2Count = 1;    /* dec'd to 0 below, forces loop exit */
  832.                             break;
  833.                         case unionOne:
  834.                             *prevDestSOR = workSORSpace;
  835.                             break;
  836.                         case firstIsLow:
  837.                             if (destCount == maxAllocated)
  838.                                 GrowSI(tempHandle, &maxAllocated, &newSIP, &destSOR, &prevDestSOR);
  839.                             *destSOR = *sor2;
  840.                             destCount++;
  841.                             prevDestSOR = destSOR++;
  842.                             break;
  843.                         case secondIsLow:
  844.                             GXPostGraphicsError(internal_layout_error);
  845.                             sor2Count = 1;    /* dec'd to 0 below, forces loop exit */
  846.                             break;
  847.                         } /* end switch */
  848.                     sor2++;
  849.                     sor2Count--;
  850.                     break;
  851.                 } /* end switch */
  852.             } /* end still have both */
  853.         else if (sor1Count > 0)
  854.             { /* still have first */
  855.             switch (UnionOne(prevDestSOR, sor1, &workSORSpace))
  856.                 {
  857.                 case unionAll:
  858.                     newSIP->data.range.ranges[0] = workSORSpace;
  859.                     destCount = 1;
  860.                     sor1Count = 1;    /* dec'd to 0 below, forces loop exit */
  861.                     sor2Count = 0;
  862.                     break;
  863.                 case unionOne:
  864.                     *prevDestSOR = workSORSpace;
  865.                     break;
  866.                 case firstIsLow:
  867.                     if (destCount == maxAllocated)
  868.                         GrowSI(tempHandle, &maxAllocated, &newSIP, &destSOR, &prevDestSOR);
  869.                     *destSOR = *sor1;
  870.                     destCount++;
  871.                     prevDestSOR = destSOR++;
  872.                     break;
  873.                 case secondIsLow:
  874.                     GXPostGraphicsError(internal_layout_error);
  875.                     sor1Count = 1;    /* dec'd to 0 below, forces loop exit */
  876.                     break;
  877.                 } /* end switch */
  878.             sor1++;
  879.             sor1Count--;
  880.             } /* end still have first */
  881.         else
  882.             { /* still have second */
  883.             switch (UnionOne(prevDestSOR, sor2, &workSORSpace))
  884.                 {
  885.                 case unionAll:
  886.                     newSIP->data.range.ranges[0] = workSORSpace;
  887.                     destCount = 1;
  888.                     sor1Count = 0;
  889.                     sor2Count = 1;    /* dec'd to 0 below, forces loop exit */
  890.                     break;
  891.                 case unionOne:
  892.                     *prevDestSOR = workSORSpace;
  893.                     break;
  894.                 case firstIsLow:
  895.                     if (destCount == maxAllocated)
  896.                         GrowSI(tempHandle, &maxAllocated, &newSIP, &destSOR, &prevDestSOR);
  897.                     *destSOR = *sor2;
  898.                     destCount++;
  899.                     prevDestSOR = destSOR++;
  900.                     break;
  901.                 case secondIsLow:
  902.                     GXPostGraphicsError(internal_layout_error);
  903.                     sor2Count = 1;    /* dec'd to 0 below, forces loop exit */
  904.                     break;
  905.                 } /* end switch */
  906.             sor2++;
  907.             sor2Count--;
  908.             } /* end still have second */
  909.         } /* end main merge loop */
  910.     
  911.     HUnlock((Handle) dest);
  912.     HUnlock((Handle) source);
  913.     
  914.     newSize = siSize + destCount * sizeof(SelectionOffsetRange);
  915.     SetHandleSize((Handle) dest, newSize);
  916.     if (MemError() != noErr) GXPostGraphicsError(out_of_memory);
  917.     destSIP = *dest;
  918.     
  919.     destSIP->type = (SelectionType) ((destCount == 1) ? simpleRange : discontiguousRange);
  920.     destSIP->data.range.rangeCount = destCount;
  921.     sor1 = &newSIP->data.range.ranges[0];
  922.     destSOR = &destSIP->data.range.ranges[0];
  923.     while (destCount-- > 0) *destSOR++ = *sor1++;
  924.     
  925.     DisposeHandle(tempHandle);
  926.     
  927.     } /* UnionSelection */
  928.  
  929. /* ---------------------------------------------------------------------------------- */
  930.  
  931. void SectSelection(SelectionHandle dest, SelectionHandle source)
  932.     {
  933.     SelectionHandle sourceCopy = source;
  934.     if (HandToHand((Handle *) &sourceCopy) != noErr)
  935.         {GXPostGraphicsError(out_of_memory); return;}
  936.     InvertSelection(dest);
  937.     InvertSelection(sourceCopy);
  938.     UnionSelection(dest, sourceCopy);
  939.     DisposeHandle((Handle) sourceCopy);
  940.     InvertSelection(dest);
  941.     } /* SectSelection */
  942.  
  943. /* ---------------------------------------------------------------------------------- */
  944.  
  945. void XorSelection(SelectionHandle dest, SelectionHandle source)
  946.     {
  947.     SelectionHandle sourceCopy = source;
  948.     if (HandToHand((Handle *) &sourceCopy) != noErr)
  949.         {GXPostGraphicsError(out_of_memory); return;}
  950.     SectSelection(sourceCopy, dest);
  951.     UnionSelection(dest, source);
  952.     DiffSelection(dest, sourceCopy);
  953.     DisposeHandle((Handle) sourceCopy);
  954.     } /* XorSelection */
  955.  
  956. /* ---------------------------------------------------------------------------------- */
  957.  
  958. void DiffSelection(SelectionHandle dest, SelectionHandle source)
  959.     {
  960.     SelectionHandle sourceCopy = source;
  961.     if (HandToHand((Handle *) &sourceCopy) != noErr)
  962.         {GXPostGraphicsError(out_of_memory); return;}
  963.     InvertSelection(sourceCopy);
  964.     SectSelection(dest, sourceCopy);
  965.     DisposeHandle((Handle) sourceCopy);
  966.     } /* DiffSelection */
  967.  
  968. /* ---------------------------------------------------------------------------------- */
  969.  
  970. gxShape GetLayoutSelection(
  971.     gxShape                     layout,
  972.     SelectionHandle selection,
  973.     SelectionOffset lineStart,
  974.     gxHighlightType     highlightType,
  975.     gxCaretType             caretType)
  976.  
  977.     {
  978.     long                                    maxOffset;
  979.     SelectionOffset             off1, off2;
  980.     SelectionOffsetRange    *sor;
  981.     SelectionPtr                    destSIP;
  982.     gxShape                                 retShape, workShape;
  983.     short                                 count;
  984.     
  985.     if (layout == nil || selection == nil || (destSIP = *selection) == nil)
  986.         {GXPostGraphicsError(parameter_is_nil); return nil;}
  987.     
  988.     HLock((Handle) selection);
  989.     maxOffset = GXGetLayout(layout, nil, nil, nil, nil, nil, nil, nil, nil, nil);
  990.     
  991.     switch (destSIP->type)
  992.         {
  993.         case emptySelection:
  994.             retShape = GXNewShape(gxEmptyType);
  995.             break;
  996.         case simpleCaret:
  997.             off1 = destSIP->data.caret.offset - lineStart;
  998.             if (off1 < 0) off1 = 0;
  999.             else if (off1 > maxOffset) off1 = maxOffset;
  1000.             retShape = GXGetLayoutCaret(
  1001.                 layout,
  1002.                 (gxByteOffset) off1,
  1003.                 highlightType,
  1004.                 caretType,
  1005.                 nil);
  1006.             break;
  1007.         case simpleRange:
  1008.             if (destSIP->data.range.ranges[0].minOffset == selectionExtremeEdge) off1 = 0;
  1009.             else
  1010.                 {
  1011.                 off1 = destSIP->data.range.ranges[0].minOffset - lineStart;
  1012.                 if (off1 < 0) off1 = 0;
  1013.                 else if (off1 > maxOffset) off1 = maxOffset;
  1014.                 }
  1015.             if (destSIP->data.range.ranges[0].maxOffset == selectionExtremeEdge) off2 = maxOffset;
  1016.             else
  1017.                 {
  1018.                 off2 = destSIP->data.range.ranges[0].maxOffset - lineStart;
  1019.                 if (off2 < 0) off2 = 0;
  1020.                 else if (off2 > maxOffset) off2 = maxOffset;
  1021.                 }
  1022.             retShape = GXGetLayoutHighlight(
  1023.                 layout,
  1024.                 (gxByteOffset) off1,
  1025.                 (gxByteOffset) off2,
  1026.                 highlightType,
  1027.                 nil);
  1028.             break;
  1029.         case discontiguousRange:
  1030.             /* In this case, we need to build a multi-gxPolygon gxPolygons gxShape. */
  1031.             count = (short) destSIP->data.range.rangeCount;
  1032.             sor = &destSIP->data.range.ranges[0];
  1033.             retShape = GXNewShape(gxEmptyType);
  1034.             workShape = GXNewShape(gxEmptyType);
  1035.             while (count--)
  1036.                 { /* gather loop */
  1037.                 if (sor->minOffset == selectionExtremeEdge) off1 = 0;
  1038.                 else
  1039.                     {
  1040.                     off1 = sor->minOffset - lineStart;
  1041.                     if (off1 < 0) off1 = 0;
  1042.                     else if (off1 > maxOffset) off1 = maxOffset;
  1043.                     }
  1044.                 if (sor->maxOffset == selectionExtremeEdge) off2 = maxOffset;
  1045.                 else
  1046.                     {
  1047.                     off2 = sor->maxOffset - lineStart;
  1048.                     if (off2 < 0) off2 = 0;
  1049.                     else if (off2 > maxOffset) off2 = maxOffset;
  1050.                     }
  1051.                 GXGetLayoutHighlight(
  1052.                     layout,
  1053.                     (gxByteOffset) off1,
  1054.                     (gxByteOffset) off2,
  1055.                     highlightType,
  1056.                     workShape);
  1057.                 GXUnionShape(retShape, workShape);
  1058.                 sor++;
  1059.                 } /* end gather loop */
  1060.             GXDisposeShape(workShape);
  1061.             break;
  1062.         } /* end switch */
  1063.     
  1064.     HUnlock((Handle) selection);
  1065.     return retShape;
  1066.     } /* GetLayoutSelection */
  1067.  
  1068. /* NewDiscontiguousSelection creates a discontiguous selection. It returns an empty
  1069.         selection if the argument is nil. */
  1070.  
  1071. SelectionHandle NewDiscontiguousSelection(SelectionRanges *ranges)
  1072.     {
  1073.     long                                    i;
  1074.     SelectionHandle             returnedSelection, workingSelection;
  1075.     SelectionOffsetRange    *thisSOR;
  1076.     
  1077.     returnedSelection = NewEmptySelection();
  1078.     
  1079.     if (ranges)
  1080.         {
  1081.         thisSOR = ranges->ranges;
  1082.         workingSelection = NewRangeSelection(thisSOR++);
  1083.         
  1084.         for (i = ranges->rangeCount - 1; i >= 0; --i)
  1085.             {
  1086.             UnionSelection(returnedSelection, workingSelection);
  1087.             SetRangeSelection(workingSelection, thisSOR++);
  1088.             }
  1089.         
  1090.         DisposeSelection(workingSelection);
  1091.         }
  1092.     
  1093.     return returnedSelection;
  1094.     } /* NewDiscontiguousSelection */
  1095.